package secondriver.process.handler;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Objects;
import net.coobird.thumbnailator.Thumbnails;
import secondriver.process.core.ImageEntry;
import secondriver.process.core.Setting;
import secondriver.process.core.cache.Cache;
import secondriver.process.exception.ArgumentInvalidException;
import secondriver.process.exception.NotFoundImageSourceException;
import secondriver.process.exception.ServerInnerException;
import spark.Request;
import spark.Response;
import spark.Route;
import spark.utils.StringUtils;

import static secondriver.process.handler.Param.*;

/**
 * Author : secondriver
 * Date :  2015/12/29
 */
public class ImageProcess implements Route {

    private Cache cache;

    private Setting setting;

    public ImageProcess(Setting setting) {
        this.setting = setting;
    }

    public void setCache(Cache cache) {
        Objects.requireNonNull(cache, "Cache can't be null.");
        this.cache = cache;
    }

    @Override
    public Object handle(Request request, Response response) throws Exception {
        try {

            String source = validFunction(request.queryParams(SOURCE.paramName));

            final ImageEntry imageEntry = getImageEntry(source);
            final Thumbnails.Builder builder = Thumbnails.of(imageEntry.getImage());

            //处理源
            source(builder, request, imageEntry);

            //旋转
            rotate(builder, request);

            //水印
            watermark(builder, request);

            //结果输出
            OutputType outputType = outputTypeFunction(request.queryParams(OUTPUT_TYPE.paramName));
            switch (outputType) {
                case BASE64: {
                    outputWithBase64(builder, imageEntry, response);
                    break;
                }
                case URL: {
                    outputWithUrl(builder, imageEntry, response);
                    break;
                }
                default: {
                    outputWithStream(builder, imageEntry, response);
                    break;//Stream
                }
            }
        } catch (MalformedURLException | ArgumentInvalidException e) {
            throw new ArgumentInvalidException(e.getMessage());
        } catch (IOException e) {
            throw new ServerInnerException(e.getMessage());
        }
        return null;
    }

    private void outputWithStream(Thumbnails.Builder builder, ImageEntry imageEntry, Response response) throws IOException {
        response.type(imageEntry.getMimeType().getContentType());
        builder.toOutputStream(response.raw().getOutputStream());
    }

    private void outputWithBase64(Thumbnails.Builder builder, ImageEntry imageEntry, Response response) throws IOException {
        Path path = null;
        try {
            path = Paths.get(setting.getCacheFileLocation(),
                    "base64_temp_" + System.currentTimeMillis() + "." + imageEntry.getMimeType()
                            .getFormatName());
            builder.toFile(path.toFile());
            Base64.Encoder encoder = Base64.getEncoder();
            response.body("data:" + imageEntry.getMimeType().getContentType() + ";base64," + encoder.encodeToString(Files.readAllBytes(path)));
        } finally {
            if (path != null) {
                Files.deleteIfExists(path);
            }
        }
    }

    private void outputWithUrl(Thumbnails.Builder builder, ImageEntry imageEntry, Response response) throws IOException {
        String filename = System.currentTimeMillis() + "." + imageEntry.getMimeType().getFormatName();
        Path path = Paths.get(setting.getExternalStaticFileLocation(), filename);
        builder.toFile(path.toFile());
        response.redirect("/" + filename);
    }

    private ImageEntry getImageEntry(String source) {
        ImageEntry imageEntry;
        if (Objects.isNull(cache)) {
            imageEntry = ImageEntry.create(source);
        } else {
            imageEntry = cache.get(source);
            if (Objects.isNull(imageEntry)) {
                imageEntry = ImageEntry.create(source);
            }
        }
        if (!imageEntry.isComplete()) {
            imageEntry.fetch();
            if (imageEntry.isComplete()) {
                if (!Objects.isNull(cache) && imageEntry.isRemoteSource()) {
                    cache.put(source, imageEntry);
                }
            } else {
                throw new NotFoundImageSourceException(
                        "Source='" + source + "' can't fetch image from third server or local server .");
            }
        }
        return imageEntry;
    }

    private void rotate(final Thumbnails.Builder builder, Request request) {
        String rotateAngle = request.queryParams(ROTATE_ANGLE.paramName);
        if (StringUtils.isNotEmpty(rotateAngle)) {
            builder.rotate(rotateAngleFunction(rotateAngle));
        }
    }

    private void crop(final Thumbnails.Builder builder, Request request) {
        String cropPositions = request.queryParams(CROP_POSITION.paramName);
        if (StringUtils.isNotEmpty(cropPositions)) {
            builder.crop(positionsFunction(cropPositions));
        }
    }

    private void watermark(Thumbnails.Builder builder, Request request) {
        String watermark = request.queryParams(WATERMARK.paramName);
        if (StringUtils.isNotEmpty(watermark)) {
            ImageEntry imageEntry = getImageEntry(watermark);
            builder.watermark(
                    positionsFunction(request.queryParams(WATERMARK_POSITION.paramName)),
                    imageEntry.getImage(),
                    watermarkOpacityFunction(request.queryParams(WATERMARK_OPACITY.paramName))
            );
        }
    }

    private void source(final Thumbnails.Builder builder, Request request, ImageEntry imageEntry) {
        try {
            ImageDimensionMode mode = modeFunction(request.queryParams(MODE.paramName));
            if (ImageDimensionMode.SIZE == mode) {
                ModeValue<Number> sizeValue = modeValueFunction(request.queryParams(MODE_VALUE.paramName), () ->
                        new ModeValue<>(Integer.MAX_VALUE, Integer.MAX_VALUE));
                builder.size(sizeValue.width.intValue(), sizeValue.height.intValue())
                        .keepAspectRatio(keepAspectRatioFunction(request.queryParams(KEEP_ASPECT_RATIO.paramName)));
                crop(builder, request);
            } else {
                ModeValue<Number> scaleRatio = modeValueFunction(request.queryParams(MODE_VALUE.paramName), () -> new
                        ModeValue<>(1.0, 1.0));
                builder.scale(scaleRatio.width.doubleValue(), scaleRatio.height.doubleValue());
            }
            builder.outputQuality(qualityFunction(request.queryParams(QUALITY.paramName)))
                    .useExifOrientation(true)
                    .outputFormat(imageEntry.getMimeType().getFormatName());
        } catch (IllegalArgumentException e) {
            throw new ArgumentInvalidException("Builder occur illegal argument " + e.getMessage());
        }
    }
}